#include <AST/expr/var/VarAST.hpp>
#include <global.hpp>
#include <AST/expr/literal/IntAST.hpp>

// 引用一个已经定义的变量
Var::Var(string name) : name(std::move(name)) {
    assert(this->name != "" && "Var::Var: var name should not be empty");
}

// 定义一个新的变量
Var::Var(
    string name,
    bool isDefinition,
    bool isConst,
    bool isNullable,
    Expr *initExpr,
    Type *type)
    : name(std::move(name)),
      isDefinition(isDefinition),
      isConst(isConst),
      isNullable(isNullable),
      initExpr(initExpr),
      type(type) {
    assert(this->name != "" && "Var::Var: var name should not be empty");

    // 调用本构造函数，意味着必须是定义
    assert(isDefinition == true);

    // 常量必须被初始化
    assert(!(isConst && initExpr == nullptr));

    // 不允许类型和初始化同时为空
    assert(!(type == nullptr && initExpr == nullptr));

    // XXX 类型未指定，则自动推导类型
    // 延迟到 codegen 阶段，才能知道 initExpr 的类型
}

// 根据形参生成一个模板变量
Var::Var(const llvm::Argument *arg)
    : name(arg->getName()),
      isDefinition(false),
      // FIXME: 检测Const性
      isConst(false),
      initExpr(nullptr),
      isNullable(true),
      type(arg->getType()) {
    assert(this->name != "" && "Var::Var: var name should not be empty");
}

llvm::Value *Var::codegen() {
    // 情况：变量引用
    if (!isDefinition) return valCodegen();
    // 情况：变量定义
    return defCodegen();
}


llvm::AllocaInst *Var::createEntryAlloca(Function *fun) {
    if (type == nullptr) {
        logError("Var::CreateEntryAlloca: var has no known type");
        return nullptr;
    }
    llvm::IRBuilder<> tmpBuilder(
        &fun->getEntryBlock(), fun->getEntryBlock().begin());
    return tmpBuilder.CreateAlloca(type, 0, name);
}

llvm::AllocaInst *Var::createAlloca() {
    if (type == nullptr) {
        logError("Var::createAlloca: var has no known type");
        return nullptr;
    }
    return builder.CreateAlloca(type, 0, name);
}

const std::string &Var::getName() const {
    return name;
}
llvm::Value *Var::refCodegen() {
    Value *V = theScope.findValue(name);
    type = V->getType()->getPointerElementType();
    if (!type->isArrayTy()) {
        // 如果是基础类型，则不需要解引
        return V;
    } else {
        return V;
    }
}

llvm::Value *Var::valCodegen() {
    Value *V = theScope.findValue(name);
    if (V == nullptr)
        return logError("Var::valCodegen: unknown vaiable name: " + name);
    // 更新type
    // 注意类型是否正确
    type = V->getType()->getPointerElementType();
    if (!type->isArrayTy()) {
        // 如果是基础类型，则需要解一次引用
        return builder.CreateLoad(type, V, name);
    } else {
        // 如果时数组，不解引用
        return V;
    }
}

llvm::Value *Var::defCodegen() {
    // 看有没有初始值
    Value *initVal =
        initExpr != nullptr ? initExpr->codegen() : BasicType::getDefault(type);
    if (initVal == nullptr) return nullptr;
    // 检查声明类型是否吻合
    if (type != nullptr && type != initVal->getType()) {
        print_t(type);
        print_t(initVal->getType());
        return logError(
            "Var::codegen: The declared and defined types do not match");
    }
    // 得到确定的类型
    if (type == nullptr) type = initVal->getType();
    // 申请栈空间
    AllocaInst *alloc =
        createEntryAlloca(builder.GetInsertBlock()->getParent());
    // 储存初始值
    if (!type->isAggregateType())
        builder.CreateStore(initVal, alloc);
    // 添加到符号表
    if (!theScope.insertValue(name, alloc)) {
        return logError("Var::defCodegen: redefinition found: " + name);
    }
    // 定义语句返回Void
    return UndefValue::get(BasicType::VoidTy);
}

llvm::Type *Var::updateTypeByVal(const Value *val) {
    if (type != nullptr) assert(type == val->getType());
    else
        type = val->getType();
    return type;
}

llvm::Type *Var::updateTypeByPtr(const Value *ptr) {
    if (type != nullptr)
        assert(type == ptr->getType()->getPointerElementType());
    else
        type = ptr->getType()->getPointerElementType();
    return type;
}
